//
// Created by Administrator on 2021/10/21.
//

#include "MRenderer.h"

static MRenderer *instance;
static bool showDemoGUI = Config::SHOW_DEMO_UI;

MRenderer::MRenderer() {
    log_debug("create MRenderer Instance, title = %s, width = %d, height = %d", Config::WINDOW_TITLE.c_str(), Config::WINDOW_WIDTH, Config::WINDOW_WIDTH);

    // create window.
    createWindow();

    // compile shaders
    ShaderUtil::initShaders();
}

MRenderer::MRenderer(const String title, const int width, const int height) {
    log_debug("create MRenderer Instance, title = %s, width = %d, height = %d", title.c_str(), width, height);
    Config::WINDOW_TITLE = title;
    Config::WINDOW_WIDTH = width;
    Config::WINDOW_HEIGHT = height;

    // create window.
    createWindow();

    // compile shaders
    ShaderUtil::initShaders();
}

/**
 * 获得实例
 * @return mRender单例对象
 */
MRenderer *MRenderer::getInstance(const String title, const int width, const int height) {
    if(instance) return instance;
    instance = new MRenderer(title, width, height);
    return instance;
}

/**
 * 渲染场景
 * @param p_scene 场景
 */
void MRenderer::render(Scene *p_scene) {
    // 初始化
    init(p_scene);

    // 渲染循环
    renderLoop();

    // 结束，资源回收
    terminate();
}

/**
 * 初始化窗口和场景
 * @param _scene 场景
 */
void MRenderer::init(Scene* _scene) {
    scene = _scene;
    log_debug("MRenderer instance onInit scene, %s", scene -> name.c_str());

    // onInit scene
    initScene();

    // onInit GUI
    initImGUI();

    //初始化视口和绑定窗口大小发生改变时监听
    framebuff_size_callback(window, Config::WINDOW_WIDTH, Config::WINDOW_HEIGHT);
    glfwSetFramebufferSizeCallback(window, framebuff_size_callback);
    glfwSetKeyCallback(window, key_callback);
    glfwSetMouseButtonCallback(window, mouse_callback);
}

/**
 * 执行渲染流程
 */
void MRenderer::renderLoop() {
    log_info("start MRenderer renderLoop!, %s", scene -> name.c_str());

    // start main loop
    float elapsedTime = 0;

    // state
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);
    // 支持分屏
    glEnable(GL_SCISSOR_TEST);
    //抗锯齿
//    glEnable (GL_LINE_SMOOTH);
//    glHint (GL_LINE_SMOOTH, GL_NICEST);

    while (!glfwWindowShouldClose(window)) {
        //time
        elapsedTime = (float) glfwGetTime();

        //初始化
        glClearColor(0.1f, 0.2f, 0.2f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        //绘制场景
        renderScene(elapsedTime);

        //绘制GUI
        renderImGUI(elapsedTime);

        //交换缓存
        glfwSwapBuffers(window);
        glfwPollEvents();
    }
}

/**
 * 创建window
 * */
void MRenderer::createWindow() {
    //初始化GLFW
    if (!glfwInit()) {
        log_error("GLFW初始化失败");
        return;
    }
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, Config::GL_MAJORVERSION);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, Config::GL_MINORVERSION);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    window = glfwCreateWindow(Config::WINDOW_WIDTH, Config::WINDOW_HEIGHT, Config::WINDOW_TITLE.c_str(), NULL, NULL);
    MAssert::check(window, "create window was failed!");
    glfwMakeContextCurrent(window);

    //将指针交给GLAD管理
    if (!gladLoadGLLoader((GLADloadproc) glfwGetProcAddress)) {
        log_error("GLAD指针管理初始化失败");
        return;
    }
}

/**
 * 初始化场景
 */
void MRenderer::initScene() {
    MAssert::check(scene);
    scene->onInit();
}

void MRenderer::renderScene(float elapsedTime) {
    MAssert::check(scene);
    Camera * camera = scene->getActiveCamera();
    MAssert::check(camera);

    Matrix4x4 proj = camera->getProjectionMatrix();
    Matrix4x4 view = camera->getViewMatrix();
    Matrix4x4 sceneWorldMatrix;

    std::vector<Object3D *> objectList = scene->getChildrenList();
    MAssert::check(!objectList.empty(), "场景为空");
    glViewport(0, 0, Config::WINDOW_WIDTH, Config::WINDOW_HEIGHT);
    glScissor(0, 0, Config::WINDOW_WIDTH, Config::WINDOW_HEIGHT);
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    for (auto iterator = objectList.begin(); iterator != objectList.end(); ++iterator) {
        Object3D* it = *iterator;
        if(it->bTickable && it != scene->getActiveCamera()){
            it->onRender(elapsedTime, proj, view, sceneWorldMatrix);
        }
    }

    // 相机预览
    for (auto iterator = objectList.begin(); iterator != objectList.end(); ++iterator) {
        Camera * it_camera = dynamic_cast<Camera*>(*iterator);
        if(!MAssert::isNull(it_camera) && it_camera->bPreview){
            glViewport(Config::WINDOW_WIDTH - 200, 0, 200, 200);
            glScissor(Config::WINDOW_WIDTH - 200, 0, 200, 200);
            glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
            Matrix4x4 it_sceneWorldMatrix;
            for (auto c_iterator = objectList.begin(); c_iterator != objectList.end(); ++c_iterator) {
                auto c_it = *c_iterator;
                if(c_it->bTickable && c_it != it_camera) {
                    c_it->onRender(elapsedTime, it_camera->getProjectionMatrix(), it_camera->getViewMatrix(), it_sceneWorldMatrix);
                }
            }
        }
    }

    scene->onRender(elapsedTime, proj, view, sceneWorldMatrix);
}

/**
 * 初始化GUI
 */
void MRenderer::initImGUI() {
    IMGUI_CHECKVERSION();
    ImGui::CreateContext();
    ImGui::StyleColorsDark();
    ImGui_ImplGlfw_InitForOpenGL(window, true);
    ImGui_ImplOpenGL3_Init("#version 330");
    ImGuiIO &io = ImGui::GetIO();
    (void) io;
    io.Fonts->AddFontFromFileTTF("C:\\Windows\\Fonts\\consola.ttf", 13.0f, NULL, io.Fonts->GetGlyphRangesDefault());
    //io.Fonts->AddFontFromFileTTF("C:\\Windows\\Fonts\\msyh.ttf", 16.0f, NULL, io.Fonts->GetGlyphRangesChineseSimplifiedCommon());

    sceneUi = new MainGui();
    sceneUi->setObject3D(scene);
}

/**渲染imGUI*/
void MRenderer::renderImGUI(float elapsedTime) {
    // Start the Dear ImGui frame
    ImGui_ImplOpenGL3_NewFrame();
    ImGui_ImplGlfw_NewFrame();
    ImGui::NewFrame();

    if (showDemoGUI) {
        ImGui::ShowDemoWindow(&showDemoGUI);
    }

    //scene ui
    if(!MAssert::isNull(sceneUi) && sceneUi->bTickable){
        Camera * camera = scene->getActiveCamera();
        MAssert::check(camera);

        Matrix4x4 sceneWorldMatrix;
        sceneUi->onRender(elapsedTime, camera->getProjectionMatrix(), camera->getViewMatrix(), sceneWorldMatrix);
    }

    ImGui::Render();
    ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
}


/**
 * 按键回调
 * @param window 上下文
 * @param key 按键code
 * @param scancode scancode
 * @param action 动作，按下/抬起
 * @param mods 模式
 */
void key_callback(GLFWwindow *window, int key, int scancode, int action, int mods) {
    //log_info("key_callback, key=%d, scancode=%d, action=%d,mods=%d", key, scancode, action, mods);
}

/**
 * 鼠标回调
 * @param window 上下文
 * @param button 鼠标按键
 * @param action One of `GLFW_PRESS` or `GLFW_RELEASE`.
 * @param mods mods
 */
void mouse_callback(GLFWwindow * window, int button, int action, int mods) {
    //log_info("mouse_callback, key=%d, action=%d,mods=%d", button, action, mods);
    if(GLFW_MOUSE_BUTTON_LEFT == button && GLFW_PRESS == action){
        double cx, cy;
        glfwGetCursorPos(window, &cx, &cy);

        //屏幕空间
        Vector2 p5(cx, cy);
        //屏幕空间转NDC空间
        float p4x = (2 * cx) / (Config::WINDOW_WIDTH - 1);
        float p4y = (1 - (2 * cy)) / Config::WINDOW_HEIGHT;
        Vector2 p4(p4x, p4y);
        //NDC转投影空间 todo
//        log_info("////////////////////////////////////////");
//        log_info("屏幕空间x=%.3f,y=%.3f", cx, cy);
//        log_info("NDC空间x=%.3f,y=%.3f", p4x, p4y);
//        log_info("////////////////////////////////////////");
    }
}

/**
 *  @param[in] pWwindow The window whose framebuffer was resized.
 *  @param[in] width The new width, in pixels, of the framebuffer.
 *  @param[in] height The new height, in pixels, of the framebuffer.
 */
void framebuff_size_callback(GLFWwindow *window, int width, int height) {
    log_info("window size callback, w=%d, h=%d", width, height);
    Config::WINDOW_WIDTH = width;
    Config::WINDOW_HEIGHT = height;
    //glViewport(0, 0, width, height);

    Camera* activeCamera = Scene::getStaticActiveCamera();
    if(!MAssert::isNull(activeCamera) && activeCamera->bFramebuffSizeCallback){
        activeCamera->left = - (width / 100.0f);
        activeCamera->right = (width / 100.0f);
        activeCamera->bottom = - (height / 100.0f);
        activeCamera->top = (height / 100.0f);
    }
}


void MRenderer::terminate() {
    log_info("关闭");
    //4.关闭
    glfwTerminate();
    ImGui_ImplOpenGL3_Shutdown();
    ImGui_ImplGlfw_Shutdown();
    ImGui::DestroyContext();

    SAFE_DELETE(sceneUi);
    ShaderUtil::releaseShaders();
    SAFE_DELETE(instance);
}



